{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "iris_tensorflow.ipynb",
      "provenance": [],
      "collapsed_sections": []
    },
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.6.8"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "IiTxCwB7t6Ef",
        "colab_type": "text"
      },
      "source": [
        "# Training an Iris classifier using TensorFlow\n",
        "\n",
        "_WARNING: you are on the master branch, please refer to the examples on the branch that matches your `cortex version`_\n",
        "\n",
        "In this notebook, we'll show how to train a classifier trained on the [iris data set](https://archive.ics.uci.edu/ml/datasets/iris) using TensorFlow."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "j6QdLAUpuW7r",
        "colab_type": "text"
      },
      "source": [
        "## Install Dependencies\n",
        "First, we'll install our dependencies:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "BQE5z_kHj9jV",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "pip install tensorflow==1.14.* scikit-learn==0.21.* boto3==1.*"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yEVK-sLnumqn",
        "colab_type": "text"
      },
      "source": [
        "## Load the data\n",
        "We can use scikit-learn to load the Iris dataset:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "tx9Xw0x0lfbl",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "from sklearn.datasets import load_iris\n",
        "from sklearn.model_selection import train_test_split\n",
        "\n",
        "iris = load_iris()\n",
        "X, y = iris.data, iris.target\n",
        "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.8, random_state=42)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "obGdgMm3urb2",
        "colab_type": "text"
      },
      "source": [
        "## Train the model\n",
        "We'll use TensorFlow's [`DNNClassifier`](https://www.tensorflow.org/versions/r1.14/api_docs/python/tf/estimator/DNNClassifier) to train the model:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "jjYp8TaflhW0",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "import tensorflow as tf\n",
        "\n",
        "feature_names = [\"sepal_length\", \"sepal_width\", \"petal_length\", \"petal_width\"]\n",
        "\n",
        "\n",
        "def train_input_fn(features, labels, batch_size):\n",
        "    irises = {}\n",
        "\n",
        "    for i, feature_name in enumerate(feature_names):\n",
        "        irises[feature_name] = features[:, i]\n",
        "        \n",
        "    dataset = tf.data.Dataset.from_tensor_slices((irises, labels))\n",
        "    dataset = dataset.shuffle(1000).repeat().batch(batch_size)\n",
        "\n",
        "    return dataset\n",
        "\n",
        "\n",
        "def eval_input_fn(features, labels, batch_size):\n",
        "    irises = {}\n",
        "    for i, feature_name in enumerate(feature_names):\n",
        "        irises[feature_name] = features[:, i]\n",
        "\n",
        "    if labels is None:\n",
        "        inputs = irises\n",
        "    else:\n",
        "        inputs = (irises, labels)\n",
        "\n",
        "    dataset = tf.data.Dataset.from_tensor_slices(inputs)\n",
        "    dataset = dataset.batch(batch_size)\n",
        "\n",
        "    return dataset\n",
        "\n",
        "\n",
        "feature_columns = [tf.feature_column.numeric_column(feature_name) for feature_name in feature_names]\n",
        "\n",
        "classifier = tf.estimator.DNNClassifier(\n",
        "    feature_columns=feature_columns,\n",
        "    hidden_units=[10, 10],\n",
        "    n_classes=3,\n",
        ")\n",
        "\n",
        "classifier.train(input_fn=lambda: train_input_fn(X_train, y_train, 100), steps=1000)\n",
        "\n",
        "eval_result = classifier.evaluate(input_fn=lambda: eval_input_fn(X_test, y_test, 100))\n",
        "\n",
        "print(\"\\nTest set accuracy: {accuracy:0.3f}\\n\".format(**eval_result))  # Accuracy should be > 90%"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Hdwu-wzJvJLb",
        "colab_type": "text"
      },
      "source": [
        "## Export the model\n",
        "Now we can export the model using [`Estimator.export_saved_model`](https://www.tensorflow.org/versions/r1.14/api_docs/python/tf/estimator/Estimator#export_saved_model):"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "AVgs2mkdllRn",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def json_serving_input_fn():\n",
        "    placeholders = {}\n",
        "    features = {}\n",
        "    for feature_name in feature_names:\n",
        "        placeholders[feature_name] = tf.placeholder(shape=[None], dtype=tf.float64, name=feature_name)\n",
        "        features[feature_name] = tf.expand_dims(placeholders[feature_name], -1)\n",
        "    \n",
        "    return tf.estimator.export.ServingInputReceiver(features, receiver_tensors=placeholders)\n",
        "\n",
        "\n",
        "classifier.export_saved_model(\"export\", json_serving_input_fn)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ipVlP4yPxFxw",
        "colab_type": "text"
      },
      "source": [
        "## Upload the model to AWS\n",
        "\n",
        "Cortex loads models from AWS, so we need to upload the exported model."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3IqsfyylxLhy",
        "colab_type": "text"
      },
      "source": [
        "Set these variables to configure your AWS credentials and model upload path:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "lc9LBH1uHT_h",
        "colab_type": "code",
        "cellView": "form",
        "colab": {}
      },
      "source": [
        "AWS_ACCESS_KEY_ID = \"\" #@param {type:\"string\"}\n",
        "AWS_SECRET_ACCESS_KEY = \"\" #@param {type:\"string\"}\n",
        "S3_UPLOAD_PATH = \"s3://my-bucket/iris-classifier/tensorflow\" #@param {type:\"string\"}\n",
        "\n",
        "import sys\n",
        "import re\n",
        "\n",
        "if AWS_ACCESS_KEY_ID == \"\":\n",
        "    print(\"\\033[91m{}\\033[00m\".format(\"ERROR: Please set AWS_ACCESS_KEY_ID\"), file=sys.stderr)\n",
        "\n",
        "elif AWS_SECRET_ACCESS_KEY == \"\":\n",
        "    print(\"\\033[91m{}\\033[00m\".format(\"ERROR: Please set AWS_SECRET_ACCESS_KEY\"), file=sys.stderr)\n",
        "\n",
        "else:\n",
        "    try:\n",
        "        bucket, key = re.match(\"s3://(.+?)/(.+)\", S3_UPLOAD_PATH).groups()\n",
        "    except:\n",
        "        print(\"\\033[91m{}\\033[00m\".format(\"ERROR: Invalid s3 path (should be of the form s3://my-bucket/path/to/file)\"), file=sys.stderr)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NXeuZsaQxUc8",
        "colab_type": "text"
      },
      "source": [
        "Upload the model to S3:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "YLmnWTEVsu55",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "import os\n",
        "import boto3\n",
        "\n",
        "s3 = boto3.client(\"s3\", aws_access_key_id=AWS_ACCESS_KEY_ID, aws_secret_access_key=AWS_SECRET_ACCESS_KEY)\n",
        "\n",
        "for dirpath, _, filenames in os.walk(\"export\"):\n",
        "    for filename in filenames:\n",
        "        filepath = os.path.join(dirpath, filename)\n",
        "        filekey = os.path.join(key, filepath[len(\"export/\"):])\n",
        "        print(\"Uploading s3://{}/{}...\".format(bucket, filekey), end = '')\n",
        "        s3.upload_file(filepath, bucket, filekey)\n",
        "        print(\" ✓\")",
        "\n",
        "print(\"\\nUploaded model export directory to \" + S3_UPLOAD_PATH)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "aR-mmcUzyCV3",
        "colab_type": "text"
      },
      "source": [
        "<!-- CORTEX_VERSION_MINOR -->\n",
        "That's it! See the [example on GitHub](https://github.com/cortexlabs/cortex/tree/master/examples/tensorflow/iris-classifier) for how to deploy the model as an API."
      ]
    }
  ]
}
